﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Dynamic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using MdDocGenerator.CodeTemplate;
using MdDocGenerator.Extensions;
using MdDocGenerator.Util;
using Mysoft.Map6.Core.EntityBase;
using Mysoft.Map6.Core.Pipeline;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Formatting = Newtonsoft.Json.Formatting;

namespace MdDocGenerator
{
    public class MarkdownGenerator
    {
        private Dictionary<string, MemberNode> _memberNodes;
        private readonly Dictionary<Type, string> _aliasSet = new Dictionary<Type, string>
        {
            {typeof(int),"int"},
            {typeof(int?),"int"},
            {typeof(string),"string" },
            {typeof(decimal),"decimal" },
            {typeof(decimal?),"decimal" },
            {typeof(DateTime),"DateTime" },
            {typeof(DateTime?),"DateTime" },
            {typeof(bool),"bool" },
            {typeof(bool?),"bool" },
            {typeof(ArrayList),"List" },
            {typeof(List<>),"List" }
        };
        private static readonly Dictionary<Type, object> ValueProvider = new Dictionary<Type, object>()
        {
            {typeof(string),"string"},
            {typeof(DateTime),DateTime.Now.ToString("yyyy-MM-dd")},
            {typeof(decimal),123.12M},
            {typeof(Guid),$"{Guid.NewGuid().ToString().ToUpper()}"},
            {typeof(bool),true },
            {typeof(char),"1" },
            {typeof(Enum),1 },
            {typeof(EntityState),1 },
            {typeof(double),123.00D },
            {typeof(int),1},
        };

        /// <summary>
        /// 生成文档
        /// </summary>
        /// <param name="dlls"></param>
        /// <param name="destPath"></param>
        public void Generate(List<string> dlls, string destPath)
        {
            var outputModule = ConfigHelper.OutputModule.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList();
            dlls.RemoveAll(t => outputModule.Any(o => t.IndexOf(o) >= 0) == false);
            //1、获取需要生成文档的方法
            var methods = AssemblyHelper.GetMethods(dlls);

            //2、加载注释信息
            _memberNodes = XmlDocCommentHelper.LoadMemberNodeToDictionary(dlls);

            //3、循环输出文档
            var sumaryMdPath = GetSUMMARYMD(destPath);
            StringBuilder sumSb = new StringBuilder();
            foreach (var methodInfo in methods)
            {
                if (methodInfo.ReflectedType == null) continue;
                if (methodInfo.DeclaringType == null) continue;
                try
                {
                    bool isObsolete = methodInfo.GetCustomAttributes(typeof(ObsoleteAttribute), false).Length == 1;
                    //有作废特性

                    var fullName = $"M:{methodInfo.ReflectedType.FullName}.{methodInfo.Name}";
                    var summary = GetSummary(fullName);
                    var fileName = GetOutputFileName(summary, methodInfo, destPath, isObsolete);
                    using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.ReadWrite, FileShare.Read))
                    {
                        var content = new MarkdownBuilder();
                        content.AppendLine(GenerateSummary(summary, isObsolete));
                        content.AppendLine(GenerateRequestInfo(methodInfo));
                        content.AppendLine(GenerateFunctionRemark(fullName));

                        var paramList = methodInfo.GetParameters();
                        content.AppendLine(GenerateParamTable(paramList, fullName));
                        content.AppendLine(GenerateParamSample(paramList));

                        content.AppendLine(GenerateReturnInfo(methodInfo, fullName));

                        var buffer = Encoding.UTF8.GetBytes(content.ToString());
                        fs.Write(buffer, 0, buffer.Length);
                    }
                    sumSb.AppendLine($"* [{methodInfo.Name}]({methodInfo.ReflectedType.Name}/{methodInfo.ReflectedType.Name}_{methodInfo.Name}.md) ");
                }
                catch (Exception)
                {

                }
            }
            using (var sumaryfs = new FileStream(sumaryMdPath, FileMode.Create, FileAccess.ReadWrite, FileShare.Read))
            {
                var bufferSum = Encoding.UTF8.GetBytes(sumSb.ToString());
                sumaryfs.Write(bufferSum, 0, bufferSum.Length);
            }
        }

        /// <summary>
        /// 查询方法的备注、说明
        /// </summary>
        public List<Client.Model.MyMethodInfo> QueryMethodRemark(List<string> dlls)
        {
            List<Client.Model.MyMethodInfo> result = new List<Client.Model.MyMethodInfo>();
            //1、获取需要生成文档的方法
            var methods = AssemblyHelper.GetMethods(dlls);

            //2、加载注释信息
            _memberNodes = XmlDocCommentHelper.LoadMemberNodeToDictionary(dlls);
            foreach (var methodInfo in methods)
            {
                var methodFullName = $"{methodInfo.ReflectedType.FullName}.{methodInfo.Name}";
                var fullName = $"M:{methodFullName}";
                var summary = GetSummary(fullName);
                var content = new MarkdownBuilder();
                content.AppendLine(GenerateSummary(summary, false));
                content.AppendLine(GenerateFunctionRemark(fullName));
                result.Add(new Client.Model.MyMethodInfo
                {
                    MethodFullName = methodFullName,
                    Remark = content.ToString()
                });
            }
            return result;
        }

        /// <summary>
        /// 生成接口功能
        /// </summary>
        /// <param name="summary"></param>
        /// <returns></returns>
        private string GenerateSummary(string summary, bool isObsolete = false)
        {
            var builder = new MarkdownBuilder();
            builder.Header(4, $"接口功能{(isObsolete ? "`已作废`" : "")}");
            builder.Blockquotes(summary);
            return builder.ToString();
        }

        /// <summary>
        /// 生成请求信息
        /// </summary>
        /// <param name="methodInfo"></param>
        /// <returns></returns>
        private string GenerateRequestInfo(MethodInfo methodInfo)
        {
            if (methodInfo.Name.Contains("ValidateUnContract"))
            {

            }
            var builder = new MarkdownBuilder();
            builder.Header(4, "请求地址");
            builder.AppendLine($"> /api/{methodInfo.ReflectedType.FullName}/{methodInfo.Name}.aspx");
            builder.AppendLine("");

            builder.Header(4, "HTTP请求方式");
            builder.Blockquotes("POST");
            return builder.ToString();
        }

        /// <summary>
        /// 生成功能逻辑
        /// </summary>
        /// <param name="methodName"></param>
        /// <returns></returns>
        private string GenerateFunctionRemark(string methodName)
        {
            if (methodName.Contains("GetOrderAndAttrachRoomsByOrderId"))
            {

            }
            var builder = new MarkdownBuilder();
            builder.Header(4, "功能逻辑");
            builder.Append("<pre>");
            string remark = GetRemark(methodName);
            string newRemark = string.IsNullOrEmpty(remark) ? "" : GetNewRemark(remark, methodName);
            newRemark.Split(new string[] { "\r\n", Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)
                .ToList()
                .ForEach(o =>
                {
                    builder.Append(o.Replace("            ", ""));
                });
            builder.AppendLine("</pre>");
            return builder.ToString();
        }

        /// <summary>
        /// 生成参数信息
        /// </summary>
        /// <param name="paramList"></param>
        /// <param name="methodName"></param>
        /// <returns></returns>
        private string GenerateParamTable(ParameterInfo[] paramList, string methodName)
        {
            var builder = new MarkdownBuilder();
            builder.Header(4, "请求参数");
            List<string[]> items = new List<string[]>();
            foreach (var parameterInfo in paramList)
            {
                var item = new string[] { $"`{parameterInfo.Name}`", "true", $"{GetTypeAlias(parameterInfo.ParameterType)}", $"{GetParamDesc(methodName, parameterInfo.Name)}" };
                items.Add(item);
            }
            builder.Table(new[] { "参数", "必选", "类型", "说明" }, items);
            return builder.ToString();
        }

        //private string GenerateParamSampleNew(ParameterInfo[] paramList)
        //{
        //    var dic = new Dictionary<string,object>();
        //    paramList.ToList().ForEach(p =>
        //    {
        //        dic.TryAdd(p.Name,new Util.ValueBuilder().Build(p));
        //    });

        //    return JsonConvert.SerializeObject(dic);
        //}

        /// <summary>
        /// 获取请求示例
        /// </summary>
        /// <param name="paramList"></param>
        /// <returns></returns>
        private string GenerateParamSample(ParameterInfo[] paramList)
        {
            var paramRemark = new StringBuilder();
            var paramRemarkMd = new MarkdownBuilder();
            var builderMd = new MarkdownBuilder();
            var sbParam = new StringBuilder();
            sbParam.Append("{");
            var paramCount = paramList.Length;
            var index = 1;
            foreach (var p in paramList)
            {
                if (p.ParameterType.IsCustomType())
                {
                    var valueBuilder = new DefaultValueBuilder();
                    Type type;
                    if (p.ParameterType.IsAbstract)
                    {
                        var obj = EntityFactory.New(p.ParameterType);
                        type = obj.GetType();
                    }
                    else
                    {
                        type = p.ParameterType;
                    }
                    var value = valueBuilder.Build(type);
                    var paramJsonValue = JsonConvert.SerializeObject(value, Formatting.Indented);
                    sbParam.Append($"\"{p.Name}\":{paramJsonValue}");

                    paramRemark.AppendLine(new DefaultValueBuilder().GetJson(value, _memberNodes));
                }
                else
                {
                    if (ValueProvider.TryGetValue(p.ParameterType, out var foo))
                    {
                        sbParam.Append($"\"{p.Name}\":\"{foo}\"");
                    }
                    else
                    {
                        sbParam.Append($"\"{p.Name}\":null");
                    }
                }

                if (paramCount > index)
                {
                    sbParam.Append(",");
                }

                index++;
            }
            sbParam.Append("}");
            builderMd.Header(4, "请求参数示例");
            var jobj = JObject.Parse(sbParam.ToString());
            var ja = jobj.Descendants()
                .Where(t => t.Type == JTokenType.Property && TypeHelper.IsIgnoreProperty(((JProperty)t).Name))
                .ToList();
            if (ja.Any())
            {
                ja.ForEach(o => o.Remove());
            }
            builderMd.Code("json", FormatJsonString(jobj.ToString()));
            paramRemarkMd.Header(4, "请求参数示例说明");
            paramRemarkMd.AppendLine(paramRemark.ToString());
            builderMd.AppendLine(paramRemarkMd.ToString());
            return builderMd.ToString();
        }

        /// <summary>
        /// 生成返回信息
        /// </summary>
        /// <param name="methodInfo"></param>
        /// <param name="methodName"></param>
        /// <returns></returns>
        private string GenerateReturnInfo(MethodInfo methodInfo, string methodName)
        {
            var content = new MarkdownBuilder();
            content.Header(4, "返回值");
            var retType = methodInfo.ReturnType;
            content.List($"类型 {GetTypeAlias(retType)}");
            content.List($"说明 {GetReturns(methodName)}");
            content.Header(4, "返回结果示例");
            content.AppendLine();
            if (retType.Name == "Void")
            {
                content.Append("无");
                return content.ToString();
            }

            if (retType.Name == "Tuple`1")
            {
                return content.ToString();
            }


            if (ValueProvider.TryGetValue(retType, out object foo))
            {
                content.Code("", foo?.ToString());
                return content.ToString();
            }
            var paramRemarkMd = new MarkdownBuilder();
            paramRemarkMd.Header(4, "返回结果示例说明");
            if (typeof(Entity).IsAssignableFrom(retType))
            {
                var obj = EntityFactory.New(retType);
                content.Code("json", JsonConvert.SerializeObject(obj, Formatting.Indented));
                paramRemarkMd.AppendLine(new DefaultValueBuilder().GetJson(obj, _memberNodes));
                content.AppendLine(paramRemarkMd.ToString());
                return content.ToString();
            }

            if (retType.IsGenericType)
            {
                var obj = Activator.CreateInstance(retType);
                content.Code("json", JsonConvert.SerializeObject(obj, Formatting.Indented));
                paramRemarkMd.AppendLine(new DefaultValueBuilder().GetJson(obj, _memberNodes));
                content.AppendLine(paramRemarkMd.ToString());
                return content.ToString();
            }

            content.Append("无");
            return content.ToString();
        }

        /// <summary>
        /// 获取输出文件名称
        /// </summary>
        /// <param name="summary"></param>
        /// <param name="methodInfo"></param>
        /// <returns></returns>
        private string GetOutputFileName(string summary, MethodInfo methodInfo, string destPath, bool isObsolete = false)
        {
            summary = summary ?? "";
            var fileName = Path.Combine(($"{destPath}\\{FormatFileName(methodInfo.DeclaringType?.Name ?? "未分类")}"), FormatFileName($"{(isObsolete ? "[已作废]" : "")}{methodInfo?.DeclaringType?.Name}_{methodInfo?.Name}.md"));
            var dir = Directory.GetParent(fileName).FullName;
            if (Directory.Exists(dir) == false)
            {
                Directory.CreateDirectory(dir);
            }
            Console.WriteLine($"start output -->{fileName}");
            return fileName;
        }

        private string GetSUMMARYMD(string destPath)
        {
            var fileName = Path.Combine(destPath, "SUMMARY.md");
            var dir = Directory.GetParent(fileName).FullName;
            if (Directory.Exists(dir) == false)
            {
                Directory.CreateDirectory(dir);
            }
            return fileName;
        }

        /// <summary>
        /// 格式化文件名，替换文件名中不合法的字符
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        private string FormatFileName(string str) => string.IsNullOrEmpty(str) ? Guid.NewGuid().ToString() : Path.GetInvalidFileNameChars().Aggregate(str, (current, fileNameChar) => current.Replace(fileNameChar, '_'));


        /// <summary>
        /// 递归提取see的注释
        /// </summary>
        /// <param name="oldRemark"></param>
        /// <returns></returns>
        private string GetNewRemark(string oldRemark, string methodName)
        {
            Regex reg = new Regex(@"<see cref=""M:(?<see>.*?)"" />", RegexOptions.Multiline);
            var matchList = reg.Matches(oldRemark);

            foreach (Match match in matchList)
            {
                if (match.Success == false) continue;

                var matchValue = match.Value;
                var functionName = match.Groups["see"].Value;
                if (string.IsNullOrEmpty(functionName)) continue;
                //找到see的方法的备注
                string seeRemark = GetRemark(functionName);
                if (functionName.StartsWith($"{methodName}"))
                {
                    Console.ForegroundColor = ConsoleColor.Red;
                    Console.WriteLine($"see了自己，请检查{functionName}方法注释");
                    Console.ForegroundColor = ConsoleColor.White;
                    return oldRemark;
                }
                oldRemark = oldRemark.Replace(matchValue, seeRemark);
            }

            matchList = reg.Matches(oldRemark);
            if (matchList.Count > 0)
            {
                oldRemark = GetNewRemark(oldRemark, methodName);
            }
            return oldRemark;
        }

        /// <summary>
        /// 获取方法返回值说明
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        private string GetReturns(string key)
        {
            var pKey = _memberNodes.Keys.FirstOrDefault(x => x.Contains(key)) ?? "";
            return (_memberNodes.ContainsKey(pKey) ? _memberNodes[pKey].Returns : string.Empty)?.Replace("\r\n", "");
        }

        /// <summary>
        /// 获取类型别名
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        private string GetTypeAlias(Type type)
        {
            return _aliasSet.ContainsKey(type) ? _aliasSet[type] : type.Name;
        }

        /// <summary>
        /// 获取参数描述
        /// </summary>
        /// <param name="key"></param>
        /// <param name="paramName"></param>
        /// <returns></returns>
        private string GetParamDesc(string key, string paramName)
        {
            var pKey = _memberNodes.Keys.FirstOrDefault(x => x.Contains(key)) ?? "";
            if (_memberNodes.ContainsKey(pKey))
            {
                foreach (var paramNode in _memberNodes[pKey].ParamList)
                {
                    if (paramNode.Name == paramName)
                    {
                        return paramNode.Value;
                    }
                }
            }

            return string.Empty;
        }


        /// <summary>
        /// 获取接口Summary注释
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        private string GetSummary(string key)
        {
            var pKey = _memberNodes.Keys.FirstOrDefault(x => x.Contains(key)) ?? "";
            return (_memberNodes.ContainsKey(pKey) ? _memberNodes[pKey].Summary == null ? "" : (_memberNodes[pKey].Summary.Value ?? "") : string.Empty).Replace("\r\n", "").Replace(" ", "")
                .Replace("<br/>", "")
                .Replace("<br>", "");
        }

        /// <summary>
        /// 获取方法备注
        /// </summary>
        /// <returns></returns>
        private string GetRemark(string key)
        {
            var pKey = _memberNodes.Keys.FirstOrDefault(x => x.Contains(key + "("));
            pKey = pKey ?? _memberNodes.Keys.FirstOrDefault(x => x.Contains(key));
            var remark = (_memberNodes.ContainsKey(pKey ?? "") ? _memberNodes[pKey ?? ""].Remarks : string.Empty);
            return remark;
        }

        /// <summary>
        /// 格式化Json字符串
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        private string FormatJsonString(string str)
        {
            JsonSerializer serializer = new JsonSerializer();
            TextReader tr = new StringReader(str);
            JsonTextReader jtr = new JsonTextReader(tr);
            var obj = serializer.Deserialize(jtr);
            if (obj == null) return str;
            StringWriter textWriter = new StringWriter();
            JsonTextWriter jsonWriter = new JsonTextWriter(textWriter)
            {
                Formatting = Formatting.Indented,
                Indentation = 4,
                IndentChar = ' '
            };
            serializer.Serialize(jsonWriter, obj);
            return textWriter.ToString();
        }
    }
}